package com.erhya.export.poi;

import com.erhya.export.poi.annotation.ExportField;
import com.erhya.export.poi.handler.Content;
import com.erhya.export.poi.verify.VerifyData;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Blob;
import java.sql.Clob;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 抽象层-公共方法区
 *
 * @param <T> 数据泛型
 */
public abstract class AbstractExcelTemplate<T> extends BaseCacheDict implements ExcelTemplate<T> {

    /**
     * 父类字段标识
     * 当存在继承关系时，若字段名在子类查询不到，则从父类查找
     */
    private Map<String, String> superField = null;

    protected Object obj;
    protected Field field;
    protected Class<?> clazz;
    protected ExportField fieldAnnotation;
    /** 序号计数器 */
    protected AtomicInteger counter;
    /** 按照该条数进行Sheet拆分 */
    protected Integer fetchSize;
    /** 单元格是否生成下拉选择 */
    protected Boolean cellSelect;

    protected final String getObjectVal(String fieldName){

        String fieldVal = null;

        if (Map.class.isAssignableFrom(clazz)){
            fieldVal = getFieldMapVal(fieldName);
        } else {
            getField(fieldName);
            fieldVal = getFieldVal(fieldName);
        }

        // 处理映射值
        Map<String, Map<String, String>> dictMap = getDict();
        if (!CollectionUtils.isEmpty(dictMap) && dictMap.containsKey(fieldName)){
            Map<String, String> itemMap = dictMap.get(fieldName);
            fieldVal = itemMap.getOrDefault(fieldVal, fieldVal);
        }
        return fieldVal;
    }

    /**
     * 获取配置单元格的宽度
     * @param defaultVal 默认宽度
     */
    protected final Integer getCellWidth(Integer defaultVal){
        if (field != null){
            if (fieldAnnotation != null){
                return fieldAnnotation.width();
            }
        }
        return defaultVal;
    }

    /**
     * 接口默认实现方法，获取反射类字段
     *
     * @param fieldName 字段名
     * @return 字段信息
     */
    private void getField(String fieldName) {
        fieldAnnotation = null;
        try {
            // 判断字段是属于父类还是子类，减少循环中的判断
            if (!CollectionUtils.isEmpty(superField) && superField.containsKey(fieldName)){
                Class<?> superclass = clazz.getSuperclass();
                field = superclass.getDeclaredField(fieldName);
            } else {
                // 从子类中进行查找
                field = clazz.getDeclaredField(fieldName);
            }
        } catch (NoSuchFieldException e) {
            try {
                // 从父类中查找
                Class<?> superclass = clazz.getSuperclass();
                field = superclass.getDeclaredField(fieldName);
                // 懒加载，将字段存入map中，下次直接从父类中查找属性
                if (superField == null){
                    superField = new HashMap<>();
                }
                superField.put(fieldName, null);
            } catch (NoSuchFieldException ex) {
                throw new RuntimeException(ex);
            }
        }
        field.setAccessible(true);

        if (field != null){
            fieldAnnotation = field.getAnnotation(ExportField.class);
        }
    }

    /**
     * 获取map类型的参数
     */
    private String getFieldMapVal(String fieldName) {
        Object fieldVal = null;
        try {
            Method method = clazz.getMethod("get", Object.class);
            fieldVal = method.invoke(obj, fieldName);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        return fieldVal == null ? "" : String.valueOf(fieldVal);
    }

    /**
     * 接口默认实现方法，获取反射类字段值
     * @return 字段值
     */
    private String getFieldVal(String fieldName) {
        if (fieldAnnotation != null && fieldAnnotation.isNo()) {
            return String.valueOf(counter.addAndGet(1));
        }
        String fieldType = field.getGenericType().getTypeName();
        String fieldVal = null;

        if (fieldAnnotation != null){
            Class<? extends Content>[] classes = fieldAnnotation.contentHandler();
            if (classes.length > 0){
                 return getContentHandler(classes[0], obj, fieldName);
            }
        }

        try {
            Object o = field.get(obj);
            if (o != null) {
                if (StringUtils.isNotEmpty(fieldType)) {
                    switch (fieldType) {
                        case "java.util.Date":
                            String format = fieldAnnotation == null ? "yyyy-MM-dd HH:mm:ss" : fieldAnnotation.dateFormat();
                            fieldVal = VerifyData.verify((Date) o, format);
                            break;
                        case "java.sql.Clob":
                            Clob clob = (Clob) o;
                            fieldVal = VerifyData.verify(clob);
                            break;
                        case "java.sql.Blob":
                            Blob blob = (Blob) o;
                            fieldVal = VerifyData.verify(blob);
                            break;
                        case "java.math.BigDecimal":
                            if (fieldAnnotation == null || fieldAnnotation.scale() == null){
                                fieldVal = ((BigDecimal) o).toPlainString();
                                break;
                            }
                            int scale = fieldAnnotation.scale()[0];
                            int round = fieldAnnotation.scale()[1];
                            fieldVal = VerifyData.verify((BigDecimal) o, scale, round);
                            break;
                        default:
                            fieldVal = String.valueOf(o);
                            try {
                                throw new RuntimeException("存在无法解析的数据类型：" + fieldVal + "，请实现Content接口进行处理");
                            } catch (RuntimeException e) {

                            }
                    }
                }
            }

        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        return StringUtils.isEmpty(fieldVal) ? "" : fieldVal;
    }

    public static <T extends Content> String getContentHandler(Class<T> clazz, Object key, String fieldName) {
        String invoke = null;
        try {
            Method method = clazz.getDeclaredMethod("content", Object.class, String.class);
            Object mevoke = method.invoke(clazz.newInstance(),key,fieldName);
            invoke = mevoke.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return invoke;
    }

    @Override
    public void put(String field, String key, String value) {
        Map<String, Map<String, String>> dictMap = getDict();
        if (!dictMap.containsKey(field)){
            Map<String, String> dictItem = new LinkedHashMap<>();
            dictItem.put(key, value);
            dictMap.put(field, dictItem);
            setDictMap(dictMap);
            return;
        }
        Map<String, String> fieldMap = dictMap.get(field);
        fieldMap.put(key, value);
        dictMap.put(field, fieldMap);
        setDictMap(dictMap);
    }

    public void setFetchSize(Integer fetchSize) {
        this.fetchSize = fetchSize;
    }

    public void setCellSelect(Boolean cellSelect) {
        this.cellSelect = cellSelect;
    }
}
